#include "server.h"

#include <pthread.h>
#include <stdint.h>

#include <cstdio>
#include <iostream>
#include <memory>

using std::cerr;
using std::endl;

Server::Server(const char* hostname, uint16_t port)
  : hostname_(hostname), port_(port) {
  this->Init();
}

void Server::Init() {
  if (!this->socket.Create()) {
    cerr << __LINE__ << " : Unable to correct socket." << endl;
    return;
  }

  if (!this->socket.Bind(this->port())) {
    cerr << __LINE__ << " : Unable to bind to port." << endl;
    return;
  }

  if (!this->socket.Listen()) {
    cerr << __LINE__ << " : Unable to listen on socket." << endl;
    return;
  }
}

Server::~Server() {
  this->socket.Close();
}

namespace {
/**
 * @brief function that is used to create a pthread
 * when a new connection comes in.  This then calls the
 * Server::HandleRequest(Socket&) callback that subclasses
 * of servers can implement to do whatever they desire with.
 *
 * @param param that is actually an array of void*
 * void[0] is a Server* of who called this function
 * void[1] is the Socket* that the new connection is running on. 
 *
 * @return NULL
 */
void* HandleRequest(void* param) {
  void** data = reinterpret_cast<void**>(param);
  Server* server = reinterpret_cast<Server*>(data[0]);
  std::auto_ptr<Socket> socket(reinterpret_cast<Socket*>(data[1]));
  delete[] data;
  server->HandleRequest(socket.get());
  return NULL;
}
}  // namespace

void Server::AcceptConnections() {
  pthread_attr_t attr;
  int rv;  // return value
  rv = pthread_attr_init(&attr);
  if (rv != 0) {
    cerr << __FILE__ << ":" << __LINE__ <<
      " pthread_attr_init returned " << rv << endl;
    return;
  }

  rv = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  if (rv != 0) {
    cerr << "pthread_attr_setdetachstate returned " << rv << endl;
    return;
  }

  pthread_t thread_id;
  while (1) {
    Socket* client_sock = new Socket();
    if ((this->socket).Accept(*client_sock)) {
      void** data = new void*[2];
      data[0] = this;
      data[1] = client_sock;

      rv = pthread_create(&thread_id, &attr, ::HandleRequest,
          reinterpret_cast<void*>(data));
      if (rv != 0) {
        cerr << __FILE__ << ":" << __LINE__ <<
          " ERROR pthread_create returned " << rv << endl;
      }
    }
  }
}

